/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algorithm;

import java.util.ArrayList;
import java.util.PriorityQueue;

/**
 *
 * @author Lu Modifier la classe pour permettre de générer des solutions en
 * prenant les tâches soit dans un ordre aléatoire, soit de laplus longue à la
 * plus courte, soit dans un ordre précis
 */
public class GlouGlouAlgo {

    private ProblemData data;
//    private Task[] sortedTasks;
    private PriorityQueue<Task> sortedTasks;
    private int[] ressourcesCumulatedTime;
    private ArrayList<WorkingTask> currentState;
    private ArrayList<ArrayList<WorkingTask>> solution;
    private int k;

    public GlouGlouAlgo(ProblemData data) {
        this.data = data;
        this.ressourcesCumulatedTime = new int[data.getPolyRessourceNb()];
        this.sortedTasks = new PriorityQueue<Task>();
        this.currentState = new ArrayList<WorkingTask>();
        this.solution = new ArrayList<ArrayList<WorkingTask>>();
        for (int i = 0; i < data.getPolyRessourceNb(); i++) {
            solution.add(new ArrayList<WorkingTask>());
        }
    }

    public void runRandom() {
        // We sort the tasks randomly
        sortTasksRandomly(data.getTasks());
        runAlgo();
    }

    public void run() {
        // We sort the tasks we have, from the longest to the shortest
        sortTasks(data.getTasks());
        runAlgo();
    }

    private void runAlgo() {
        // Current ressource used
        int currentRessource;
        // The current time
        int currentTime;
        // The current task chosen
        Task currentTask = sortedTasks.remove();
        // ArrayList of tasks unusable during an iteration
        ArrayList<Task> unusableTasks;
        // We look for a suitable task
        while (currentTask != null) {
            // Boolean true if no ressources are available
            boolean nothingAvailable = false;
            unusableTasks = new ArrayList<>();
            // We select the ressource with the lowest time used
            currentRessource = getLowestRessourceTime();
            // We try to find a suitable task, if the current task selected cannot be used...
            while (isBlockedTask(currentTask) && !nothingAvailable) {
                // ... we add it to the unused tasks
                unusableTasks.add(currentTask);
                // We get the next task, if there are no more tasks we stop looking
                if (!sortedTasks.isEmpty()) {
                    currentTask = sortedTasks.remove();
                } else {
                    nothingAvailable = true;
                }
            }
            // We put back the tasks unused this iteration
            for (Task t : unusableTasks) {
                sortedTasks.add(t);
            }
            // If nothing was available, we create a "wait" task that lasts the time needed for a ressource to finish
            if (nothingAvailable) {
                currentTime = waitUntilRessourceIsFree(currentRessource);
            } else {
                //Else, we found a task to work on, we add it to the solution and to the working tasks
                WorkingTask taskAdded = new WorkingTask(ressourcesCumulatedTime[currentRessource], ressourcesCumulatedTime[currentRessource] + currentTask.getLength(), currentTask.getMachine(), currentTask.getProduct(), currentRessource, currentTask);
                ressourcesCumulatedTime[currentRessource] = taskAdded.getEndTime();
                solution.get(currentRessource).add(taskAdded);
                currentState.add(taskAdded);
                // update currentTime
                currentTime = Integer.MAX_VALUE;
                for (int i = 0; i < ressourcesCumulatedTime.length; i++) {
                    if (currentTime > ressourcesCumulatedTime[i]) {
                        currentTime = ressourcesCumulatedTime[i];
                    }
                }
            }
            for (int i = currentState.size() - 1; i >= 0; i--) {
                if (currentState.get(i).getEndTime() <= currentTime) {
                    // y passe
                    currentState.remove(i);
                }
            }
            if (!sortedTasks.isEmpty()) {
                currentTask = sortedTasks.remove();
            } else {
                currentTask = null;
            }
        }
    }

    private void sortTasks(int[][] tasks) {
        for (int i = 0, k = 0; i < data.getProductNb(); i++) {
            for (int j = 0; j < data.getMachineNb(); j++, k++) {
                sortedTasks.add(new Task(tasks[i][j], j, i));
            }
        }

    }

    private int getLowestRessourceTime() {
        int lowest = ressourcesCumulatedTime[0];
        int index = 0;
        for (int i = 0; i < data.getPolyRessourceNb() - 1; i++) {
            if (lowest > ressourcesCumulatedTime[i + 1]) {
                lowest = ressourcesCumulatedTime[i + 1];
                index = i + 1;
            }
        }
        return index;
    }

    private boolean isBlockedTask(Task currentTask) {
        for (WorkingTask w : currentState) {
            if (currentTask.getMachine() == w.getMachineUsed() || currentTask.getProduct() == w.getProductUsed()) {
                return true;
            }
        }
        return false;
    }

    public String toString() {
        String s = "Solution : \n";
        for (int i = 0; i < solution.size(); i++) {
            for (WorkingTask w : solution.get(i)) {
                s = s.concat(w.getStartTime() + "-" + w.getEndTime() + "(m=" + w.getMachineUsed() + "p=" + w.getProductUsed() + ")" + " | ");
            }
            s = s.concat("\n");
        }
        return s;
    }

    private int waitUntilRessourceIsFree(int currentRessource) {
        int newTime = Integer.MAX_VALUE;
        for (int i = 0; i < solution.size(); i++) {
            // if this isn't the current ressource and this is the minimum cumuleted time
            if (i != currentRessource && (ressourcesCumulatedTime[i]) < newTime && (ressourcesCumulatedTime[currentRessource] < ressourcesCumulatedTime[i])) {
                // then we're going to wait until then
                newTime = ressourcesCumulatedTime[i];
            }
        }
        // we update the current ressource time
        ressourcesCumulatedTime[currentRessource] = newTime;
        return newTime;
    }

    private void sortTasksRandomly(int[][] tasks) {
        // We'll save the real length of each task
        int[][] realLength = new int[data.getProductNb()][data.getMachineNb()];
        Task[][] tasksPointers = new Task[data.getProductNb()][data.getMachineNb()];
        int range = data.getProductNb() * data.getMachineNb() * 10;
        // We will generate a random length for each so that they will be sorted by this length when added to the Queue
        int randomLength;
        for (int i = 0, k = 0; i < data.getProductNb(); i++) {
            for (int j = 0; j < data.getMachineNb(); j++, k++) {
                realLength[i][j] = tasks[i][j];
                randomLength = (int) ((double) range * Math.random());
                tasksPointers[i][j] = new Task(randomLength, j, i);
                sortedTasks.add(tasksPointers[i][j]);
            }
        }

        // We put back the real lengths
        for (int i = 0, k = 0; i < data.getProductNb(); i++) {
            for (int j = 0; j < data.getMachineNb(); j++, k++) {
                tasksPointers[i][j].setLength(realLength[i][j]);
            }
        }

    }

    public ProblemData getData() {
        return data;
    }

    public ArrayList<ArrayList<WorkingTask>> getSolution() {
        return solution;
    }
}
